// 导入 axios
import axios from 'axios'
import store from '@/store/index'
import { Toast } from 'vant'
import { refreshUserToken } from '@/api/user'
import router from '@/router/index'

// 创建 axios 请求实例
const request = axios.create({
  // 基准路径
  baseURL: 'http://toutiao.itheima.net',
})

// 在 request axios 实例上配置请求拦截器
request.interceptors.request.use(config => {
  console.log('请求拦截器1')
  console.log('进入了请求拦截器, 请求配置对象：', config)

  // 所有的请求在发出去之前，都会先经过这里，在这里就可以对请求配置做修改
  // 如果登陆了，那么就自动将 token 注入到请求头中，那么在 api 封装发送请求时，就不需要配置这个 token 请求头了
  if (store.getters['user/isLogin']) {
    // 是否为刷新用户 token 请求，如果是就要使用 Vuex 中的 refresh_token 值作为 Authorization 请求头的值
    if (config.isRefreshUserToken) {
      config.headers.Authorization = `Bearer ${store.state.user.userToken.refresh_token}`
    }
    // 如果不是，就使用 Vuex 中普通的 token 值作为 Authorization 请求头的值
    else {
      // config.headers ==> { a: 1, b:2 }
      // 这里不能直接赋值
      // config.headers = {
      //   Authorization: `Bearer ${store.state.user.userToken.token}`,
      // }
      config.headers.Authorization = `Bearer ${store.state.user.userToken.token}`
    }
  }

  // 注意：在请求拦截器中一定要返回这个请求配置，不然真正发出去的请求就拿不到配置了
  // 因为 axios 内部会调用请求拦截器函数，获取其返回值，作为最新的请求配置
  return config
})

request.interceptors.request.use(config => {
  console.log('请求拦截器2')
  // 如果为 undefined，说明没有配置，默认就显示
  // 如果为 true，那更不用讲
  if (config.showLoading === undefined || config.showLoading === true) {
    // 发出请求前，先显示加载中，转圈圈
    Toast.loading({
      message: '加载中...',
      forbidClick: true,
      duration: 0, // 持续时间, 0 表示一直不关闭，默认 2000ms 后会自动关闭
    })
  }
  return config
})

// 在 request 这个 axios 实例上配置响应拦截器
// 我们不关心成功的响应拦截器，所以传 undefined，表示使用默认的成功处理（传递响应数据）
request.interceptors.response.use(undefined, err => {
  console.log('响应拦截器1-failure')
  console.log('响应拦截器触发了，错误回调，错误对象：', err)

  // 通过观察，后台接口如果响应失败，并且响应状态码为 401 时，则表示 token 失效
  // 如果我们能够在错误回调函数中拿到这个 响应状态码，就能判断出 是因为什么原因失败的
  // -- 如果是 401，就去刷新 token
  // -- 如果不是，则弹出错误消息
  if (err.response.status === 401) {
    console.log('去刷新token')
    // 发起请求刷新 token，刷新完成后，token 从无效变成有效了

    // 刷新 token 的主要流程：页面发起请求（req1） --> token 失效 --> 错误的响应拦截器（401） --> 刷新token --> 更新 vuex 和 localStorage 中的 token --> 再次发送上一次失败的请求（req1）（抠出关键的配置项（请求方法、请求地址、请求体参数、查询参数）） --> 页面逻辑
    return refreshUserToken().then(
      res => {
        // 将最新的 token 保存到 vuex 和 localStorage 中
        console.log(res, '刷新 token 的响应数据')
        store.commit('user/changeUserToken', res.data.data)
        // 刷新完 token 后，还要发起上一次失败的请求
        // 你咋知道上一次发的是什么请求呢？
        // -- 可以通过错误的响应拦截器抓到的错误对象中拿的上一次请求的配置对象，有了这个请求配置对象，就可以再次发送请求了
        // -- err.config 就是上一次请求的配置对象，拿出来再发一遍就行了
        const { method, url, data, params } = err.config // 抠出需要的配置项
        return request({ method, url, data, params })
      },
      () => {
        // 如果刷新token都失败了，那么就没法子了，只能去重新登录了
        // 在跳到登录页之前需要先清空 userToken
        store.commit('user/changeUserToken', null)
        router.push({ name: 'login' })
        // 页面上的逻辑就不需要再执行了
        // 那么如何中断链式调用呢？（链式中断） 返回一个状态一直是 pending 的 promise 实例
        return new Promise(() => {})
      }
    )

    // 还有重新发一次 上次失败的请求
  } else {
    Toast.fail(err.response.data.message)
  }

  // 这里不能让后面的 then 进入成功，因为毕竟请求是失败的
  // 如何让后面的 .then 进入错误的回调？
  // 1. 返回一个状态会变成 rejected 的 promise 实例
  // 2. 抛出错误
  return Promise.reject(err)
})

request.interceptors.response.use(
  res => {
    console.log('响应拦截器2-successful')
    Toast.clear()
    return res
  },
  err => {
    console.log('响应拦截器2-failure')
    Toast.clear()
    return Promise.reject(err)
  }
)

// 默认导出这个 axios 实例
export default request
